/*
 * \brief  Server framework
 * \author Norman Feske
 * \date   2006-04-28
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _INCLUDE__BASE__SERVER_H_
#define _INCLUDE__BASE__SERVER_H_

#include <base/thread.h>
#include <base/errno.h>
#include <base/ipc.h>
#include <base/object_pool.h>
#include <base/lock.h>
#include <cap_session/cap_session.h>

namespace Genode {

	/*
	 * A 'Server_object' is a locally implemented object that
	 * can be referenced from the outer world using a capability.
	 * The capability gets created when attaching a 'Server_object'
	 * to a 'Server_entrypoint'.
	 */
	class Server_object : public Object_pool<Server_object>::Entry
	{
		private:

			Lock _dispatch_lock;

		public:

			virtual ~Server_object() { }

			/*
			 * Serialize access with dispatch loop
			 *
			 * These methods are used for the destruction of
			 * server objects. They are exclusively used by
			 * 'Server_activation_base::entry()' and
			 * 'Server_entrypoint::dissolve()'.
			 * Never use this lock for other purposes.
			 */

			void lock()   { _dispatch_lock.lock(); }
			void unlock() { _dispatch_lock.unlock(); }

			/**
			 * Interface to be implemented by a derived class
			 *
			 * \param op   opcode of invoked method
			 * \param is   Ipc_input stream with method arguments
			 * \param os   Ipc_output stream for storing method results
			 */
			virtual int dispatch(int op, Ipc_istream &is, Ipc_ostream &os) = 0;
	};


	/**
	 * A 'Server_activation' processes one method invocation
	 * of a 'Server_object' at a time.
	 */
	class Server_entrypoint;
	class Server_activation_base: public Thread_base
	{
		private:

			Capability         _cap;
			Server_entrypoint *_ep;         /* entrypoint to which the
			                                   activation belongs */
			Msgbuf_base       *_snd_msg;
			Msgbuf_base       *_rcv_msg;

		protected:

			Server_object *_curr_obj;       /* currently dispatched server object */
			Lock           _curr_obj_lock;  /* for the protection of _curr_obj    */
			Lock           _cap_valid;      /* thread startup synchronization     */
			Lock           _delay_start;    /* delay start of request dispatching */

			Server_activation_base(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg)
			: _cap(Capability()), _ep(0), _snd_msg(snd_msg), _rcv_msg(rcv_msg),
			  _curr_obj(0), _cap_valid(Lock::LOCKED), _delay_start(Lock::LOCKED)
			{ }

		public:

			/**
			 * Set entrypoint, which the activation serves
			 *
			 * This function must by called by a 'Server_entrypoint'
			 * on 'add_activation()' only.
			 */
			void ep(Server_entrypoint *ep) { _ep = ep; }

			/**
			 * Thread interface
			 */
			void entry();

			/**
			 * Return capability to this activation
			 *
			 * This function should only be called from 'Server_entrypoint'
			 */
			Capability cap() { return _cap; }

			/**
			 * Force activation to cancel dispatching the specified server object
			 */
			void leave_server_object(Server_object *obj);
	};


	/**
	 * Entrypoint into a server
	 *
	 * For L4v2 and Linux, a 'Server_entrypoint' can hold only one activation
	 * and thus, allow only serial access to the server objects managed by
	 * this entry point. For a multithreaded server, multiple entry points
	 * (one per thread) have to be created.
	 */
	class Server_entrypoint : public Object_pool<Server_object>
	{
		private:

			Server_activation_base *_activation;
			Cap_session            *_cap_session;

		public:

			/**
			 * Constructor
			 *
			 * \param cap_session  'Cap_session' for creating capabilities
			 *                     for the server objects managed by this
			 *                     entry point
			 * \param a            initial activation
			 */
			Server_entrypoint(Cap_session *cap_session,
			                  Server_activation_base *a = 0);

			/**
			 * Add activation to activation pool of this entry point
			 */
			int add_activation(Server_activation_base *t);

			/**
			 * Associate server object with the entry point
			 */
			Capability manage(Server_object *obj);

			/**
			 * Dissolve server object from entry point
			 */
			void dissolve(Server_object *obj);
	};


	template <int STACK_SIZE>
	class Server_activation : public Server_activation_base
	{
		private:

			char _stack[STACK_SIZE];

		public:

			/**
			 * Unlock server activation if it is ready to serve requests
			 *
			 * This function must be called before using the activation,
			 * for example, by supplying the activation to an entry point.
			 */
			void start() { _delay_start.unlock(); }

			/**
			 * Constructor
			 *
			 * \param  snd_msg                message buffer for send part of IPC
			 * \param  rcv_msg                message buffer for receive part of IPC
			 * \param  start_on_construction  specifies whether the activation
			 *                                thread should be started immediately
			 *                                (default) or explicitly via the
			 *                                start() function
			 * \param  name                   name of server activation thread
			 *                                (used for debugging)
			 *
			 * The server activation thread will initialize its capability
			 * and but will not immediately enable the processing of
			 * requests. This way, the activation-using server can ensure
			 * that it gets initialized completely before the first
			 * capability invocations come in. Once the server is ready, it
			 * must enable the server activation explicitly by calling the
			 * 'start()' function. The 'start_on_construction' argument is
			 * a shortcut for the common case where the server's capability
			 * is handed over to other parties _after_ the server is
			 * completely initialized.
			 */
			Server_activation(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg,
			                  bool start_on_construction = true,
			                  const char *name = "activation")
			: Server_activation_base(snd_msg, rcv_msg)
			{

				/* start execution of server activation thread */
				Thread_base::_start((void *)&_stack[STACK_SIZE - sizeof(umword_t)], name);

				/* wait until capability gets defined */
				_cap_valid.lock();

				/* start immediate request handling if specified */
				if (start_on_construction)
					start();
			}

	};
}

#endif /* _INCLUDE__BASE__SERVER_H_ */
